package com.chenfan.mcn.controller;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.common.exception.BusinessException;
import com.chenfan.mcn.annotations.ActionTracing;
import com.chenfan.mcn.constant.McnConstant;
import com.chenfan.mcn.constant.NumberCst;
import com.chenfan.mcn.constant.SeparatorCst;
import com.chenfan.mcn.enums.ExcutionApprovalStatusEnum;
import com.chenfan.mcn.enums.OperateLockEnum;
import com.chenfan.mcn.enums.SettlementMethodEnum;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.exception.RepeatedDataExeception;
import com.chenfan.mcn.extension.Res;
import com.chenfan.mcn.extension.excel.ExcelCustom;
import com.chenfan.common.extension.validation.groups.Modify;
import com.chenfan.common.extension.lock.DistributedLockTemplate;
import com.chenfan.mcn.model.CalculationDevided;
import com.chenfan.mcn.model.ExcuteFeedback;
import com.chenfan.mcn.model.McnPage;
import com.chenfan.mcn.model.RepeatedExcutionOrderData;
import com.chenfan.mcn.model.common.KVPair;
import com.chenfan.mcn.model.dto.*;
import com.chenfan.mcn.model.vo.*;
import com.chenfan.mcn.service.AeRoyaltiesService;
import com.chenfan.mcn.service.BusinessCommissionService;
import com.chenfan.mcn.service.ContractExcutionOrderService;
import com.chenfan.mcn.utils.BusinessAssert;
import com.chenfan.mcn.utils.CommonUtil;
import com.chenfan.mcn.extension.excel.ExcelUtils;
import com.chenfan.common.extension.util.SmartCompletableFuture;
import com.chenfan.mcn.utils.ValidatorUtil;
import com.chenfan.privilege.common.config.SearchAuthority;
import com.chenfan.privilege.common.config.UrlAuth;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StopWatch;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * 正式执行单
 *
 * @author tangwei
 * Date:     2021/1/27 10:17
 * Description: 合同执行单
 */
@Slf4j
@RestController
@RequestMapping("/excutionOrder")
public class ExcutionOrderController {

    @Autowired
    private ContractExcutionOrderService contractExcutionOrderService;

    @Autowired
    private AeRoyaltiesService aeRoyaltiesService;

    @Autowired
    private BusinessCommissionService businessCommissionService;

    /**
     * 执行单详情
     * @param id
     * @return
     */
    @GetMapping("/detail")
    public Res<ExcutionOrderDetailVO> detail(@RequestParam Long id) {
        return Res.ok(contractExcutionOrderService.detail(id));
    }

    /**
     * 编辑
     * @param excutionOrderUpdateDTO
     * @return
     */
    @ActionTracing("编辑")
    @PostMapping("/update")
    public Res<Integer> update(@RequestBody ExcutionOrderUpdateDTO excutionOrderUpdateDTO) {
        return Res.ok(contractExcutionOrderService.update(excutionOrderUpdateDTO, true));
    }

    /**
     * 保存并提交打款
     * @param excutionOrderUpdateDTO
     * @return
     */
    @Deprecated
    @ActionTracing("保存并提交打款")
    //@PostMapping("/saveSubmit")
    @UrlAuth({"executionOrder_submit","executionOrder_saveAndSubmit"})
    public Res<Object> saveAndSubmit(@RequestBody ExcutionOrderUpdateDTO excutionOrderUpdateDTO) {
        contractExcutionOrderService.saveAndSubmit(excutionOrderUpdateDTO, false);
        return Res.ok();
    }

    /**
     * 保存并提交确认
     * @param updateDTO
     * @return
     */
    @ActionTracing("保存并提交确认")
    @PostMapping("/saveConfirm")
    @UrlAuth({"executionOrder_confirm_submit"})
    public Res<Object> saveConfirm(@Validated(Modify.class) @RequestBody ExcutionOrderUpdateDTO updateDTO) {
        if(CommonUtil.greaterThanZero(updateDTO.getOtherAmount())) {
            BusinessAssert.notBlank(updateDTO.getOtherAmountRemark(), McnErrorCode.PARAM_ERROR, "【其他加价收入说明】必填");
        }
        if(CommonUtil.greaterThanZero(updateDTO.getOtherFee())) {
            BusinessAssert.notBlank(updateDTO.getSpecialFeeRemark(), McnErrorCode.PARAM_ERROR, "【机酒差旅补贴说明】必填");
        }
        if(CommonUtil.greaterThanZero(updateDTO.getSubsidyIncomeAmount())) {
            BusinessAssert.notBlank(updateDTO.getSubsidyIncomeRemark(), McnErrorCode.PARAM_ERROR, "【其他补贴性收入说明】必填");
        }
        if(Objects.nonNull(updateDTO.getCalculationDevided())) {
            CalculationDevided cd = updateDTO.getCalculationDevided();
            BusinessAssert.isFalse(SettlementMethodEnum.isSingleRemittance(cd.getSettlementMethod()) && Objects.isNull(updateDTO.getStarAccountInfoId()), McnErrorCode.PARAM_ERROR, "【红人账户信息】必选");
            //校验内部红人执行单AE确认（应收/分成）金额
            if(SettlementMethodEnum.isReceivePayment(cd.getSettlementMethod())) {
                BusinessAssert.isTrue(Objects.nonNull(cd.getAeReceivableAmount()) && cd.getAeReceivableAmount().compareTo(BigDecimal.ZERO) >= 0 && cd.getAeReceivableAmount().scale() <= 2,
                        McnErrorCode.VERIFY_AMOUNT_ERROR, "【AE确认应收金额】须大于等于0且小数位小于等于2");
            } else if(SettlementMethodEnum.isRemittance(cd.getSettlementMethod())) {
                BusinessAssert.isTrue(Objects.nonNull(cd.getAeDevidedAmount()) && cd.getAeDevidedAmount().compareTo(BigDecimal.ZERO) >= 0 && cd.getAeDevidedAmount().scale() <= 2,
                        McnErrorCode.VERIFY_AMOUNT_ERROR, "【AE确认分成金额】须大于等于0且小数位小于等于2");
                BusinessAssert.notNull(cd.getAeCulturalTax(), "【AE确认文建税】必填！");
            }
        }
        contractExcutionOrderService.saveAndSubmit(updateDTO, true);
        return Res.ok();
    }

    /**
     * 保存并执行完成
     * @param excutionOrderUpdateDTO
     * @return
     */
    @ActionTracing("保存并执行完成")
    @PostMapping("/saveAndComplete")
    @UrlAuth({"executionOrder_complete"})
    public Res<Object> saveAndComplete(@RequestBody ExcutionOrderUpdateDTO excutionOrderUpdateDTO) {
        return DistributedLockTemplate.execute(OperateLockEnum.CONTRACT_EXCUTION_ORDER.generateKey(excutionOrderUpdateDTO.getId()), () -> {
            contractExcutionOrderService.saveAndComplete(excutionOrderUpdateDTO);
            return Res.ok();
        });
    }

    /**
     * 获取红人分成比例、系统参考分成金额
     * @param devidedAmountDTO
     * @return
     */
    @PostMapping("/getDividedRateOrAmount")
    public Res<DividedRateOrSysAmountVO> getDividedRateOrAmount(@Validated(Modify.class) @RequestBody DevidedAmountDTO devidedAmountDTO) {
        return Res.ok(contractExcutionOrderService.getOrUpdateDividedRate(devidedAmountDTO.getExcutionOrderId(), devidedAmountDTO.getRealPublishDate(), false));
    }

    /**
     * 执行单分页列表
     * @param mcnPage
     * @param excutionOrderSearchDTO
     * @return
     */
    @PostMapping("/page")
    @UrlAuth({"executionOrder_search"})
    public Res<Page<ExcutionOrderVO>> getPage(@ApiIgnore McnPage<ExcutionOrderVO> mcnPage, @SearchAuthority ExcutionOrderSearchDTO excutionOrderSearchDTO) {
        return Res.ok(contractExcutionOrderService.getPage(mcnPage, excutionOrderSearchDTO));
    }


    /**
     * 红人返点合同-弹框分页查询执行单
     * @param mcnPage
     * @param excutionOrderSearchDTO
     * @return
     */
    @PostMapping("/starRebatePage")
    public Res<Page<StarRebateExcutionOrderVO>> getStarRebatePage(@ApiIgnore McnPage<ExcutionOrderVO> mcnPage, @RequestBody ExcutionOrderSearchDTO excutionOrderSearchDTO) {
        return Res.ok(contractExcutionOrderService.getStarRebatePage(mcnPage, excutionOrderSearchDTO));
    }

    /**
     * 客户返点合同-弹框分页查询执行单
     * @param mcnPage
     * @param excutionOrderSearchDTO
     * @return
     */
    @PostMapping("/annualRebatePage")
    public Res<Page<AnnualRebateExcutionOrderVO>> getAnnualRebatePage(@ApiIgnore McnPage<ExcutionOrderVO> mcnPage, @RequestBody ExcutionOrderSearchDTO excutionOrderSearchDTO) {
        return Res.ok(contractExcutionOrderService.getAnnualRebatePage(mcnPage, excutionOrderSearchDTO));
    }

    /**
     * AE/商务提成-弹框分页查询执行单
     * @param mcnPage
     * @param excutionOrderSearchDTO
     * @return
     */
    @PostMapping("/aePage")
    public Res<Page<ExcutionOrderVO>> getAePage(@ApiIgnore McnPage<ExcutionOrderVO> mcnPage, @RequestBody ExcutionOrderSearchDTO excutionOrderSearchDTO) {
        return Res.ok(contractExcutionOrderService.getAePage(mcnPage, excutionOrderSearchDTO));
    }

    /**
     * AE/商务提成-弹框分页查询结果导出
     * @param type 提成类型：1-AE提成、2-商务提成
     * @param excutionOrderSearchDTO
     * @return
     */
    @PostMapping("/exportPreCommission/{type}")
    public Res<Boolean> exportPreCommission(@PathVariable(name = "type") Integer type, @RequestBody ExcutionOrderSearchDTO excutionOrderSearchDTO, HttpServletResponse response) {
        try {
            if(NumberCst.ONE.equals(type)) {
                //AE提成-不分页不计数
                List<AeRoyaltiesVoV2> dataList = CommonUtil.defaultList(contractExcutionOrderService.getExportCommissionData(excutionOrderSearchDTO)).stream()
                        .map(e -> {
                            AeRoyaltiesVoV2 aeRoyaltiesVoV2 = new AeRoyaltiesVoV2();
                            BeanUtils.copyProperties(e, aeRoyaltiesVoV2);
                            aeRoyaltiesVoV2.setSignDate(DateUtil.format(e.getSignDate(), McnConstant.SHORT_DATE_FORMAT));
                            aeRoyaltiesVoV2.setRealPublishDate(DateUtil.format(e.getRealPublishDate(), McnConstant.SHORT_DATE_FORMAT));
                            //aeRoyaltiesVoV2.setPaybackDate(DateUtil.format(e.getPaybackDate(), McnConstant.LONG_DATE_FORMAT));
                            aeRoyaltiesVoV2.setPaidDate(DateUtil.format(e.getPaidStarDate(), McnConstant.LONG_DATE_FORMAT));
                            return aeRoyaltiesVoV2;
                        }).collect(Collectors.toList());
                aeRoyaltiesService.assembleExportData(dataList);
                ExcelUtils.exportExcel(CommonUtil.defaultList(dataList), "AE提成", AeRoyaltiesVoV2.class, "AE提成详情预览.xlsx", response);
            } else if(NumberCst.TWO.equals(type)) {
                //商务提成
                List<BusinessCommissionDetailVoV2> dataList = CommonUtil.defaultList(contractExcutionOrderService.getExportCommissionData(excutionOrderSearchDTO)).stream()
                        .map(e -> {
                            BusinessCommissionDetailVoV2 businessCommissionDetailVoV2 = new BusinessCommissionDetailVoV2();
                            BeanUtils.copyProperties(e, businessCommissionDetailVoV2);
                            businessCommissionDetailVoV2.setSignDate(DateUtil.format(e.getSignDate(), McnConstant.SHORT_DATE_FORMAT));
                            businessCommissionDetailVoV2.setRealPublishDate(DateUtil.format(e.getRealPublishDate(), McnConstant.SHORT_DATE_FORMAT));
                            //businessCommissionDetailVoV2.setPaybackDate(DateUtil.format(e.getPaybackDate(), McnConstant.LONG_DATE_FORMAT));
                            businessCommissionDetailVoV2.setPaidDate(DateUtil.format(e.getPaidStarDate(), McnConstant.LONG_DATE_FORMAT));
                            businessCommissionDetailVoV2.setCommissionSettleStatus(e.getBusinessCommissionSettleStatus());
                            return businessCommissionDetailVoV2;
                        }).collect(Collectors.toList());
                businessCommissionService.assembleExportData(dataList);
                ExcelUtils.exportExcel(CommonUtil.defaultList(dataList), "商务提成", BusinessCommissionDetailVoV2.class, "商务提成详情预览.xlsx", response);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Res.failed(McnErrorCode.EXPORT_EXCEPTION);
        }
        return Res.ok(Boolean.TRUE);
    }

    /**
     * 红人返点合同-根据年框合同编号获取关联的执行单
     * @param yearContractCode
     * @return
     */
    @GetMapping("/getByYearCodeOfStarRebate")
    public Res<List<StarRebateExcutionOrderVO>> getByYearCodeOfStarRebate(@RequestParam String yearContractCode) {
        BusinessAssert.notBlank(yearContractCode, McnErrorCode.PARAM_ERROR, "年框合同编号不能为空");
        return Res.ok(contractExcutionOrderService.getByYearCodeOfStarRebate(yearContractCode));
    }

    /**
     * 导出执行单列表
     * @param excutionOrderSearchDTO
     * @param response
     */
    @ActionTracing("导出执行单列表")
    @PostMapping("/export")
    @UrlAuth({"executionOrder_export"})
    public void export(@SearchAuthority ExcutionOrderSearchDTO excutionOrderSearchDTO, HttpServletResponse response) {
        StopWatch watch = new StopWatch("export导出");
        //获取自定义的导出字段
        watch.start("自定义字段");
        List<ExcelCustom> excelCustomList = contractExcutionOrderService.chooseExportField(excutionOrderSearchDTO.getExportTypes());
        watch.stop();
        watch.start("查询数据");
        List<ExcutionExportVO> dataList = CommonUtil.defaultList(contractExcutionOrderService.export(excutionOrderSearchDTO));
        watch.stop();
        watch.start("导出excel");
        ExcelUtils.customExport("执行单导出.xlsx", null, "收入合同执行单", excelCustomList, ExcutionExportVO.class, dataList, response);
        watch.stop();
        log.info(watch.prettyPrint());
    }

    /**
     * 变更执行单状态
     *
     * @param statusDTO
     * @return {@link Res<Object>}
     */
    @ActionTracing("变更执行单状态")
    @PostMapping("/changeStatus")
    @UrlAuth({"executionOrder_cancellation","executionOrder_revocation","executionOrder_execute","executionOrder_confirm","executionOrder_reject"})
    public Res<Object> changeStatus(@Validated @RequestBody ExcutionOrderChangeStatusDTO statusDTO) {
        BusinessAssert.isTrue(ExcutionApprovalStatusEnum.anyMatch(statusDTO.getApprovalStatus(),
                ExcutionApprovalStatusEnum.CANCEL,
                ExcutionApprovalStatusEnum.INVALID,
                ExcutionApprovalStatusEnum.EXECUTED,//确认中撤回
                ExcutionApprovalStatusEnum.EXECUTING,
                ExcutionApprovalStatusEnum.APPROVAL,//确认并提交审批
                ExcutionApprovalStatusEnum.CONFIRM_REJECTED)
                , McnErrorCode.DATA_OPERATION_NOT_SUPPORTED);
        if(ExcutionApprovalStatusEnum.CONFIRM_REJECTED.code().equals(statusDTO.getApprovalStatus())) {
            BusinessAssert.notBlank(statusDTO.getReason(), McnErrorCode.PARAM_ERROR, "请填写确认驳回原因！");
        }
        return Res.ok(contractExcutionOrderService.changeStatus(statusDTO, true));
    }


    /**
     * 审批财务确认分成金额
     *
     * @param checkParam {@link CheckParam}
     * @return {@link Boolean}
     */
    @ActionTracing("财务审批")
    @PostMapping(value = "/share/amount", produces = {"application/json;charset=UTF-8"})
    public Res<Boolean> shareAmount(@RequestBody @Validated CheckParam checkParam) {
        return Res.ok(contractExcutionOrderService.shareAmount(checkParam));
    }

    /**
     * 客户关联的执行单信息
     * @param mcnPage
     * @param customerId
     * @return
     */
    @GetMapping("/cusExcutionOrder")
    public Res<Page<CusExcutionOrderVO>> getCusExcutionOrder(@ApiIgnore McnPage<CusExcutionOrderVO> mcnPage, @RequestParam Long customerId) {
        return Res.ok(contractExcutionOrderService.getCusExcutionOrder(mcnPage, customerId));
    }


    /**
     * 根据执行单号获取执行单总金额
     *
     * @param executeCode 执行单号
     * @return {@link BigDecimal}
     */
    @GetMapping("/getActualQuotedPrice")
    public Res<BigDecimal> getActualQuotedPrice(@RequestParam String executeCode) {
        return Res.ok(contractExcutionOrderService.getActualQuotedPrice(executeCode));
    }

    /**
     * 获取产品名称
     * @return
     */
    @GetMapping("/getProductName")
    public Res<List<String>> getProductName() {
        return Res.ok(contractExcutionOrderService.getProductName());
    }

    /**
     * 财务批量确认
     *
     * @param multipartFile
     * @return
     */
    @ActionTracing("财务批量确认")
    @PostMapping("/financeBatchConfirmDevided")
    @UrlAuth({"executionOrder_financeBatchConfirmDevided"})
    public Res<String> financeBatchConfirm(@RequestParam MultipartFile multipartFile) {
        List<ExcutionOrderBatchConfirmInputDTO> inputList = ExcelUtils.importExcel(multipartFile,  ExcutionOrderBatchConfirmInputDTO.class);
        return Res.ok(contractExcutionOrderService.financeBatchConfirm(inputList));
    }

    /**
     * 批量审批打款
     *
     * @param multipartFile
     */
    @ActionTracing("批量审批打款")
    @PostMapping("/batchApproval")
    public Res<String> financeBatchApproval(@RequestParam MultipartFile multipartFile) {
        List<ExcutionOrderBatchApprovalInputDTO> inputList = null;
        try {
            inputList = ExcelUtils.importExcel(multipartFile, ExcutionOrderBatchApprovalInputDTO.class);
        } catch (Exception e) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_FAILED);
        }
        return Res.ok(contractExcutionOrderService.financeBatchApproval(inputList));
    }

    /**
     * 下载失败原因
     * @param response
     * @param fileId
     */
    @GetMapping("/excel/download")
    public void excelDownload(HttpServletResponse response, String fileId) {
        log.error("收到执行单Excel下载失败文件请求，fileId={}", fileId);
        contractExcutionOrderService.excelDownload(response, fileId);
    }


    /**
     * 获取执行单计算分成关联的年框合同
     *
     * @return 年框合同
     */
    @GetMapping("/star/purchase")
    public Res<List<StarPurchaseYearFrameVO>> getStarPurchaseContract() {
        return Res.ok(contractExcutionOrderService.getStarPurchaseContract());
    }


    /**
     * 获取执行单计算分成关联的采购合同
     *
     * @param incomeContractId 收入合同id
     * @return 采购合同
     */
    @GetMapping("/purchase/contract")
    public Res<List<AssociatedPurchase>> getPurchaseContract(Long incomeContractId) {
        return Res.ok(contractExcutionOrderService.getPurchaseContract(incomeContractId));
    }

    /**
     * 是否关联采购单号
     *
     * @param state        状态（0-否，1-是）
     * @param contractCode 执行单单号
     * @return 采购单号
     */
    @GetMapping("/getPurchaseContractCode")
    public Res<String> getPurchaseContractCode(Integer state, String contractCode) {
        return Res.ok(contractExcutionOrderService.getPurchaseContractCode(state, contractCode));
    }

    /**
     * 通过执行单ID强制驳回执行单
     * @param id 执行单ID
     */
    @ActionTracing("强制驳回执行单")
    @GetMapping("/forcedReject")
    @UrlAuth({"executionOrder_forcedToDismiss"})
    public Res<Boolean> forcedReject(@RequestParam Long id) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "执行单ID不能为空！");
        return Res.ok(contractExcutionOrderService.forcedReject(id));
    }

    /**
     * 批量确认打款
     * @param dto
     * @return
     */
    @ActionTracing("批量确认打款")
    @PostMapping("/batchConfirmPaid")
    @UrlAuth({"executionOrder_batchConfirmPaid"})
    public Res<BatchConfirmPaidResultVO> batchConfirmPaid(@Validated @RequestBody BatchConfirmPaidDTO dto) {
        BusinessAssert.isTrue(dto.getPaidDate().before(new Date()), McnErrorCode.PARAM_ERROR, "打款日期不能大于当前日期！");
        return Res.ok(contractExcutionOrderService.batchConfirmPaid(dto));
    }

    /**
     * 批量提交确认
     * @param executeIds 执行单ID-多选
     * @return
     */
    @ActionTracing("批量提交确认")
    @PostMapping("/batchSubmitAndConfirm")
    @UrlAuth({"executionOrder_submit"})
    public Res<ExcutionOrderBatchSubmitVO> batchSubmitConfirm(@RequestBody List<Long> executeIds) {
        BusinessAssert.notEmpty(executeIds, McnErrorCode.PARAM_ERROR, "请至少勾选一条正式执行单！");
        List<ExcutionOrderUpdateDTO> dataList = contractExcutionOrderService.getBatchSubmitData(executeIds, ExcutionApprovalStatusEnum.EXECUTED);
        ExcutionOrderBatchSubmitVO confirmVO = new ExcutionOrderBatchSubmitVO();
        confirmVO.setTotalNum(dataList.size());
        List<CompletableFuture> completableFutureList = new ArrayList<>(dataList.size());
        List<ExcutionOrderBatchSubmitVO.FailReason> failReasonList = new CopyOnWriteArrayList<>();
        for(ExcutionOrderUpdateDTO updateDTO : dataList) {
            CompletableFuture completableFuture = SmartCompletableFuture.runAsync(() -> {
                try {
                    updateDividedRateIfNecessary(updateDTO);
                    String failReason = ValidatorUtil.getJointValidateResult(updateDTO, SeparatorCst.LINE_BREAK_HTML, Modify.class);
                    if(CommonUtil.greaterThanZero(updateDTO.getOtherAmount()) && StringUtils.isBlank(updateDTO.getOtherAmountRemark())) {
                        failReason = CommonUtil.joinWith(SeparatorCst.LINE_BREAK_HTML, failReason, "【其他加价收入说明】必填！");
                    }
                    if(CommonUtil.greaterThanZero(updateDTO.getOtherFee()) && StringUtils.isBlank(updateDTO.getSpecialFeeRemark())) {
                        failReason = CommonUtil.joinWith(SeparatorCst.LINE_BREAK_HTML, failReason, "【机酒差旅补贴说明】必填！");
                    }
                    if(CommonUtil.greaterThanZero(updateDTO.getSubsidyIncomeAmount()) && StringUtils.isBlank(updateDTO.getSubsidyIncomeRemark())) {
                        failReason = CommonUtil.joinWith(SeparatorCst.LINE_BREAK_HTML, failReason, "【其他补贴性收入说明】必填！");
                    }
                    if(Objects.nonNull(updateDTO.getCalculationDevided())) {
                        CalculationDevided cd = updateDTO.getCalculationDevided();
                        if(SettlementMethodEnum.isSingleRemittance(cd.getSettlementMethod()) && Objects.isNull(updateDTO.getStarAccountInfoId())) {
                            failReason = CommonUtil.joinWith(SeparatorCst.LINE_BREAK_HTML, failReason, "【红人账户信息】必选！");
                        }
                        if(SettlementMethodEnum.isReceivePayment(cd.getSettlementMethod()) && Objects.isNull(cd.getAeReceivableAmount())) {
                            failReason = CommonUtil.joinWith(SeparatorCst.LINE_BREAK_HTML, failReason, "【AE确认应收金额】必填！");
                        }
                        if(SettlementMethodEnum.isRemittance(cd.getSettlementMethod()) && Objects.isNull(cd.getAeDevidedAmount())) {
                            failReason = CommonUtil.joinWith(SeparatorCst.LINE_BREAK_HTML, failReason, "【AE确认分成金额】必填！");
                        }
                        if(SettlementMethodEnum.isRemittance(cd.getSettlementMethod()) && Objects.isNull(cd.getAeCulturalTax())) {
                            failReason = CommonUtil.joinWith(SeparatorCst.LINE_BREAK_HTML, failReason, "【AE确认文建税】必填！");
                        }
                    }
                    if(StringUtils.isNotBlank(failReason)) {
                        failReasonList.add(new ExcutionOrderBatchSubmitVO.FailReason(updateDTO.getId(), updateDTO.getExcuteCode(), failReason));
                    } else {
                        contractExcutionOrderService.saveAndSubmit(updateDTO, true);
                    }
                } catch (Exception e) {
                    failReasonList.add(new ExcutionOrderBatchSubmitVO.FailReason(updateDTO.getId(),
                            updateDTO.getExcuteCode(), e instanceof BusinessException ? e.getMessage() : "操作失败"));
                }
            });
            completableFutureList.add(completableFuture);
        }
        SmartCompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[]{})).join();
        confirmVO.setFailNum(failReasonList.size());
        confirmVO.setSuccessNum(dataList.size() - failReasonList.size());
        confirmVO.setFailReasonList(failReasonList);
        return Res.ok(confirmVO);
    }

    /**
     * 批量提交审批
     * @param executeIds 执行单ID-多选
     * @return
     */
    @Deprecated
    @ActionTracing("批量提交审批")
    //@PostMapping("/batchSubmitAndApproval")
    @UrlAuth({"executionOrder_submit"})
    public Res<ExcutionOrderBatchSubmitVO> batchSubmitApproval(@RequestBody List<Long> executeIds) {
        BusinessAssert.notEmpty(executeIds, McnErrorCode.PARAM_ERROR, "请至少勾选一条正式执行单！");
        List<ExcutionOrderUpdateDTO> dataList = contractExcutionOrderService.getBatchSubmitData(executeIds, ExcutionApprovalStatusEnum.CONFIRMED);
        ExcutionOrderBatchSubmitVO confirmVO = new ExcutionOrderBatchSubmitVO();
        confirmVO.setTotalNum(dataList.size());
        List<CompletableFuture> completableFutureList = new ArrayList<>(dataList.size());
        List<ExcutionOrderBatchSubmitVO.FailReason> failReasonList = new CopyOnWriteArrayList<>();
        for(ExcutionOrderUpdateDTO updateDTO : dataList) {
            CompletableFuture completableFuture = SmartCompletableFuture.runAsync(() -> {
                try {
                    updateDividedRateIfNecessary(updateDTO);
                    contractExcutionOrderService.saveAndSubmit(updateDTO, false);
                } catch (Exception e) {
                    String message = e instanceof BusinessException ? e.getMessage() : "操作失败";
                    if(e instanceof RepeatedDataExeception) {
                        RepeatedDataExeception rde = (RepeatedDataExeception)e;
                        if(rde.getState().code() == McnErrorCode.EXCUTION_ORDER_EPEATED.code()) {
                            RepeatedExcutionOrderData repeatedData = (RepeatedExcutionOrderData)rde.getData();
                            String repeatedExcutionOrderCodes = CommonUtil.defaultList(repeatedData.getRepeatedList()).stream().map(KVPair::getV).collect(Collectors.joining(SeparatorCst.COMMA));
                            message = String.format("已存在「%s」在「%s」下单的发布日期为「%s」、红人为「%s」、报价为「%s」的正式执行单，请修改后再操作。重复正式执行单号：%s",
                                    repeatedData.getCoperateBrandName(), repeatedData.getPlatformName(),
                                    repeatedData.getRealPublishDate(), repeatedData.getPlatNickName(),
                                    repeatedData.getRealAmount(), repeatedExcutionOrderCodes);
                        }
                    }
                    failReasonList.add(new ExcutionOrderBatchSubmitVO.FailReason(updateDTO.getId(),
                            updateDTO.getExcuteCode(), message));
                }
            });
            completableFutureList.add(completableFuture);
        }
        SmartCompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[]{})).join();
        confirmVO.setFailNum(failReasonList.size());
        confirmVO.setSuccessNum(dataList.size() - failReasonList.size());
        confirmVO.setFailReasonList(failReasonList);
        return Res.ok(confirmVO);
    }

    /**
     *
     * @param updateDTO
     */
    private void updateDividedRateIfNecessary(ExcutionOrderUpdateDTO updateDTO) {
        if (Objects.isNull(updateDTO.getDividedRate())) {
            ExcuteFeedback excuteFeedback = updateDTO.getExcuteFeedback();
            DividedRateOrSysAmountVO dividedAmountVO = contractExcutionOrderService.getOrUpdateDividedRate(updateDTO.getId(), excuteFeedback.getRealPublishDate(), false);
            if (Objects.nonNull(dividedAmountVO)) {
                CalculationDevided calculationDevided = updateDTO.getCalculationDevided();
                updateDTO.setDividedRate(dividedAmountVO.getDividedRate());
                updateDTO.setDividedRemark(dividedAmountVO.getDividedRemark());
                calculationDevided.setSysDevidedAmount(dividedAmountVO.getSysDevidedAmount());
            }
        }
    }
}
